package org.rcgwt.client.ui.calendar.renderer.impl;

import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.rcgwt.client.theme.CssStyle;
import org.rcgwt.client.ui.calendar.controller.CalendarAction;
import org.rcgwt.client.ui.calendar.controller.CalendarChangesMask;
import org.rcgwt.client.ui.calendar.controller.CalendarController;
import org.rcgwt.client.ui.calendar.event.ModelChangedEvent;
import org.rcgwt.client.ui.calendar.event.ModelChangedHandler;
import org.rcgwt.client.ui.calendar.i18n.CalendarUtils;
import org.rcgwt.client.ui.calendar.model.impl.CalendarEventManager;
import org.rcgwt.client.ui.calendar.model.impl.CalendarModel;
import org.rcgwt.client.ui.calendar.model.impl.Event;
import org.rcgwt.client.ui.calendar.model.impl.EventFrequency;
import org.rcgwt.client.ui.calendar.model.impl.EventStyleMapper;
import org.rcgwt.client.ui.calendar.model.impl.Period;
import org.rcgwt.client.ui.calendar.model.impl.SelectionCardinality;
import org.rcgwt.client.ui.calendar.model.impl.SelectionType;
import org.rcgwt.client.ui.calendar.mvc.ICalendarContext;
import org.rcgwt.client.ui.calendar.renderer.ICalendarGridRenderer;
import org.rcgwt.client.ui.calendar.resources.ToolTipProvider;
import org.rcgwt.client.ui.themes.ITheme;

import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HTMLTable.Cell;

/**
 * The main Calendar renderer (The Dates Grid)
 * 
 * @author Elian ORIOU
 * 
 */

@SuppressWarnings("deprecation")
public class CalendarDatesRenderer implements ICalendarGridRenderer {

	private Grid dayGrid;
	private ITheme theme;
	private Date displayedDate;

	private CalendarModel model;
	private CalendarController controller;
	private EventStyleMapper mapper;

	private int previousSelectedColumn = 0;

	private int previousSelectedRow = 0;
	private int previousSelectedCell = 0;

	private boolean isControlKeyDown;
	private boolean isShiftKeyDown;

	/**
	 * The constructor
	 */

	public CalendarDatesRenderer() {
		isControlKeyDown = false;
		isShiftKeyDown = false;
	}

	/**
	 * Component rendering method
	 */

	@Override
	public Widget render(ICalendarContext context) {

		controller = context.getController();
		model = context.getModel();
		theme = context.getTheme();
		mapper = new EventStyleMapper(theme);
		dayGrid = new Grid(7, 7);
		dayGrid.addStyleName(theme.getCalendarGridStyle().getClassName());

		/* About the current Date */
		controller.setCurrentFirstDay(CalendarUtils.getFirstDay(context
				.getModel().getCurrentYear(), context.getModel()
				.getCurrentMonth()));

		int currentYear = context.getModel().getCurrentYear();
		int currentMonth = context.getModel().getCurrentMonth();

		/* Filling days in the grid */
		fillBriefDays(dayGrid);
		fillPreviousMonthDays(dayGrid, currentYear, currentMonth);
		fillCurrentMonthDays(dayGrid, currentYear, currentMonth);
		fillNextMonthDays(dayGrid, currentYear, currentMonth);
		applyStyles(currentMonth, currentYear);

		createGridHandlers();
		configureHandlers();

		return dayGrid;
	}

	/**
	 * Creates and manages grid handlers
	 */

	private void createGridHandlers() {
		dayGrid.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				Cell c = dayGrid.getCellForEvent(event);
				if (c == null) {
					return;
				}
				int row = c.getRowIndex();
				int cell = c.getCellIndex();
				manageGridSelection(row, cell);
			}
		});
	}

	/**
	 * Configure model changed handlers
	 */

	private void configureHandlers() {
		/* Action sent by the controller (sent before by another renderer */
		controller.addModelChangedHandler(new ModelChangedHandler() {
			@Override
			public void onModelChangedEvent(ModelChangedEvent event) {
				displayedDate = event.getDate();
				int elementType = event.getElementType();

				/* To clear the grid (not only selection but all contents */
				if ((elementType & CalendarChangesMask.CLEAR_GRID) > 0) {
					resetGridData();
				}
				if ((elementType & CalendarChangesMask.CLEAR_SELECTION) > 0) {
					clearSelection();
				}
				if ((elementType & CalendarChangesMask.PREVIOUS_CONTEXT) > 0) {
					previousContextElement();
				}
				if ((elementType & CalendarChangesMask.NEXT_CONTEXT) > 0) {
					nextContextElement();
				}
				/* To select first days */
				if ((elementType & CalendarChangesMask.SELECT_FIRST_DAYS) > 0) {
					for (int i = 0; i < dayGrid.getRowCount(); i++) {
						selectCell(i, 0);
					}
				}
				/* To deselect first days */
				if ((elementType & CalendarChangesMask.UNSELECT_DAYS) > 0) {
					for (int i = 0; i < dayGrid.getColumnCount(); i++) {
						if (isSelectedCell(0, i) == true) {
							for (int j = 0; j < dayGrid.getRowCount(); j++) {
								unselectCell(j, i);
							}
						}
					}
				}
				/* To select first day */
				if ((elementType & CalendarChangesMask.SELECT_FIRST_DAY) > 0) {
					Date d = model.peekDisplayedDate();
					d.setDate(model.getCursorPosition());
					if (model.getCursorPosition() == 0) {
						d.setDate(1);
						controller.setSelection(1);
					}
					controller.setSelectedDate(d);
					selectCell(getRowForDay(model.getCursorPosition()),
							getColumnForDay(model.getCursorPosition()));
				}
				/* To select first days */
				if ((elementType & CalendarChangesMask.UNSELECT_DAY) > 0) {
					for (int i = 0; i < dayGrid.getRowCount(); i++) {
						for (int j = 0; j < dayGrid.getColumnCount(); j++) {
							unfocusWidget(dayGrid.getWidget(i, j));
						}
					}
				}
				/* To select the first week of the month */
				if ((elementType & CalendarChangesMask.SELECT_WEEK) > 0) {
					int row = model.getSelectedWeek();
					if (row == 0) {
						row = 1;
					}
					if (row > dayGrid.getRowCount()) {
						return;
					}
					for (int i = 0; i < dayGrid.getCellCount(row); i++) {
						selectCell(row, i);
					}
					controller.setSelectedWeekLine(row);
				}
				/* To deselect the previously selected week in the month */
				if ((elementType & CalendarChangesMask.UNSELECT_WEEK) > 0) {
					int row = model.getSelectedWeek();
					if (row > dayGrid.getRowCount()) {
						return;
					}
					for (int i = 0; i < dayGrid.getCellCount(row); i++) {
						unselectCell(row, i);
					}
				}
				/* To select weeks between range */
				if ((elementType & (CalendarChangesMask.SELECT_WEEK_RANGE)) > 0) {
					selectRowRange(model.getSelectedStartWeek(), model
							.getSelectedEndWeek());
				}
				/* Actions with date (Model actions) */
				if (displayedDate == null) {
					return;
				}
				int month = displayedDate.getMonth();
				int year = displayedDate.getYear() + 1900;
				/*
				 * To refresh the grid content (following the tripping of an
				 * model linked action)
				 */
				if (displayedDate != null
						&& (elementType & (CalendarChangesMask.REFRESH_GRID
								| CalendarChangesMask.SPECIFIED_MONTH
								| CalendarChangesMask.SPECIFIED_YEAR | CalendarChangesMask.SPECIFIED_MONTH)) > 0) {
					refreshGrid(year, month);
				}
				/* To apply styles on current date, event dates, and other */
				if (displayedDate != null
						&& (elementType & CalendarChangesMask.APPLY_STYLES) > 0) {
					applyStyles(month, year);
				}
			}
		});
	}

	/**
	 * Manages grid selection (when a user clicks on the grid)
	 * 
	 * @param row
	 * @param cell
	 */

	public void manageGridSelection(int row, int cell) {
		if (dayGrid.getWidget(row, cell) == null) {
			return;
		}

		/* Selection Behavior Management */
		SelectionCardinality selectionCard = model.getSelectionCardinality();
		/* No Selection case */
		if (selectionCard == SelectionCardinality.NONE) {
			Log.warn("No selection allowed !");
			return;
		}
		int sel = model.getCursorPosition();
		/* SINGLE selection case */
		if (selectionCard == SelectionCardinality.SINGLE && sel != 0) {
			int prevRow = getRowForDay(sel);
			int prevCell = getColumnForDay(sel);
			unselectCell(prevRow, prevCell);
		}

		if (isControlKeyDown == false) {
			controller.fireModelChangedEvent(CalendarChangesMask.UNSELECT_YEAR,
					null);
			controller.handle(CalendarAction.UNSELECT_WEEK_RANGE);
		}

		/* Click + SHIFT/Click week & days behavior */
		if (isShiftKeyDown == false) {
			previousSelectedColumn = cell;
		}
		/*
		 * The selection must be cleared when the user doesn't pressed the CTRL
		 * key
		 */
		clearSelection();
		unfocusWidget(getMonthDayLabel(sel));

		/* Deselection */
		if (getCurrentSelection() != null) {
			if (model.getSelectionType() == SelectionType.DATE
					&& selectionCard == SelectionCardinality.SINGLE) {
				/*
				 * If the selection is a date, and the selection mode is set on
				 * SINGLE selection : disable selection on previous selected
				 * element
				 */
				unfocusWidget(getCurrentSelection());
			} else if (model.getSelectionType() == SelectionType.MONTH
					|| model.getSelectionType() == SelectionType.YEAR) {
				/*
				 * If the selection is applied on the year, or on the month
				 * selector, any other selected element is deselected
				 */
				unfocusWidget(getCurrentSelection());
			}
		}
		/* Days Case (If the selection is MULTIPLE) */
		if (row == 0) {
			if (selectionCard != SelectionCardinality.MULTIPLE) {
				/*
				 * The multiple selection (on every specific days in a month) is
				 * forbidden
				 */
				return;
			}
			/* Changes selection type */
			controller.setSelectionType(SelectionType.DAYS);
			controller.setSelectedColumn(cell);

			/* If the user pressed the SHIFT key (multiple ranges selection) */
			if (isShiftKeyDown == true) {
				selectColumnRange(previousSelectedColumn, cell);
				return;
			}

			/* Selects days cells (or deselects if they're already selected) */
			if (isSelectedCell(row, cell) == false) {
				for (int i = 0; i < dayGrid.getRowCount(); i++) {
					selectCell(i, cell);
				}
			} else {
				for (int i = 0; i < dayGrid.getRowCount(); i++) {
					unselectCell(i, cell);
				}
			}
			return;
		}
		/* Date Case */
		controller.setSelectionType(SelectionType.DATE);
		/*
		 * Sets the selection value (the date in the month) (the cursor
		 * position)
		 */
		controller.setSelection(Integer.parseInt(((Label) dayGrid.getWidget(
				row, cell)).getText()));

		/* Sets the selected date */
		if (isShiftKeyDown == false) {
			if (model.getCursorPosition() > 7 && row == 1) {
				controller.setSelectedDate(new Date(
						model.getDisplayedYear() - 1900, model
								.getDisplayedMonth() - 1, model
								.getCursorPosition()));
				controller.handle(CalendarAction.PREVIOUS_MONTH);
				int d = model.getSelectedDate().getDate();
				selectCell(getRowForLastDay(d), getColumnForLastDay(d));
				return;
			} else if (model.getCursorPosition() < 7
					&& row == getLastFilledLine(dayGrid)) {
				controller.setSelectedDate(new Date(
						model.getDisplayedYear() - 1900, model
								.getDisplayedMonth() + 1, model
								.getCursorPosition()));
				controller.handle(CalendarAction.NEXT_MONTH);
				int d = model.getSelectedDate().getDate();
				selectCell(getRowForDay(d), getColumnForDay(d));
				return;
			} else if (isSelectable(model.getCursorPosition()) == true) {
				controller
						.setSelectedDate(new Date(
								model.getDisplayedYear() - 1900, model
										.getDisplayedMonth(), model
										.getCursorPosition()));
			}
		}
		/* MULTIPLE Selection (WITH SHIFT KEY) */
		if (isShiftKeyDown == true) {
			selectRange(previousSelectedRow, previousSelectedCell, row, cell);
			return;
		}
		previousSelectedRow = row;
		previousSelectedCell = cell;
		/* MULTIPLE Selection (WITH CONTROL KEY) */
		if (isControlKeyDown == false
				&& model.getSelectedDates().isEmpty() == false) {
			clearSelection();
			unselectCell(getRowForDay(model.getCursorPosition()),
					getColumnForDay(model.getCursorPosition()));
			if (isSelectedCell(row, cell) == true) {
				unselectCell(row, cell);
				return;
			}
			selectCell(row, cell);
			return;
		}
		/*
		 * If a days context is selected, a selection on a another day could
		 * deselect the days context selection
		 */
		if (isSelectedCell(0, cell) == true) {
			for (int i = 0; i < dayGrid.getRowCount(); i++) {
				unselectCell(i, cell);
			}
		}
		Date d = model.peekDisplayedDate();
		d.setDate(model.getCursorPosition());

		if (isSelectedCell(row, cell) == false) {
			selectCell(row, cell);
		} else {
			unselectCell(row, cell);
		}
	}

	/**
	 * Refreshes the grid content
	 * 
	 * @param year
	 * @param month
	 */

	private void refreshGrid(int year, int month) {
		fillPreviousMonthDays(dayGrid, year, month);
		fillCurrentMonthDays(dayGrid, year, month);
		fillNextMonthDays(dayGrid, year, month);
		applyStyles(month, year);
	}

	/**
	 * Renderer Update method
	 */

	@Override
	public void update(ICalendarContext context) {
		int year = model.getDisplayedYear();
		int month = model.getDisplayedMonth();
		this.refreshGrid(year, month);
	}

	/**
	 * Resets the data grid
	 */

	private void resetGridData() {
		dayGrid.clear();
		fillBriefDays(dayGrid);
	}

	/* Grid Elements rendering */

	/**
	 * Fills brief days in the grid
	 * 
	 * @param g
	 */

	private void fillBriefDays(Grid g) {
		String[] days_short = CalendarUtils.getShortDays();
		String[] days = new String[days_short.length];
		for (int i = 0; i < days.length; i++) {
			String s = days_short[i];
			days[i] = s.substring(0, 2);
		}
		/* Adding days brief on the top of the grid */
		for (int i = 0; i < days.length; i++) {
			Label d = new Label(days[i]);
			if (model.getSelectionCardinality() == SelectionCardinality.NONE
					|| model.getSelectionCardinality() == SelectionCardinality.SINGLE) {
				d.addStyleName(theme.getDisabledGridCellStyle().getClassName());
			} else {
				d.addStyleName(theme.getCalendarGridCellStyle().getClassName());
			}
			g.setWidget(0, i, d);
		}
	}

	/**
	 * Fills days of the previous month in the grid
	 * 
	 * @param g
	 * @param currentYear
	 * @param currentMonth
	 */

	public void fillPreviousMonthDays(Grid g, int year, int month) {
		int k = CalendarUtils.getPreviousMonthLastDate(year, month);

		controller.setCurrentFirstDay(CalendarUtils.getFirstDay(model
				.getDisplayedYear(), model.getDisplayedMonth()));

		for (int j = model.getCurrentFirstDay() - 1; j >= 0; j--) {
			Label d = new Label("" + k);
			d.setTitle(ToolTipProvider.getToolTipForDate(year, month - 1, k));
			if (model.getSelectionCardinality() == SelectionCardinality.NONE) {
				d.addStyleName(theme.getDisabledGridCellStyle().getClassName());
			} else {
				d.addStyleName(theme.getPreviousAndNextDayStyle()
						.getClassName());
			}
			dayGrid.setWidget(1, j, d);
			k--;
		}
	}

	/**
	 * Fills days of the current month in the grid
	 * 
	 * @param g
	 * @param currentYear
	 * @param currentMonth
	 */

	public void fillCurrentMonthDays(Grid g, int year, int month) {

		/* Adding days number to the grid */
		int firstDay = CalendarUtils.getFirstDay(year, month);
		int dayNumber = CalendarUtils.getDaysNumber(year, month);

		// 1st Row Iteration
		int k = 1;
		for (int j = firstDay; j < CalendarModel.getDaysInWeek(); j++) {
			Label l = (Label) dayGrid.getWidget(1, j);
			if (l == null) {
				l = new Label();
				dayGrid.setWidget(1, j, l);
			} else {
				removeAllStyles(l);
			}
			l.setText("" + k);
			l.setTitle(ToolTipProvider.getToolTipForDate(year, month, k));
			if (model.getSelectionCardinality() == SelectionCardinality.NONE) {
				l.addStyleName(theme.getDisabledGridCellStyle().getClassName());
			} else {
				l.addStyleName(theme.getCalendarGridCellStyle().getClassName());
			}
			if (controller.isDisplayableMin(month, year, k) == false
					|| controller.isDisplayableMax(month, year, k) == false) {
				l.addStyleName(theme.getForbiddenStyle().getClassName());
			}
			k++;
		}
		// Other rows iterations
		for (int i = 2; i < 7; i++) {
			for (int j = 0; j < CalendarModel.getDaysInWeek(); j++) {
				Label l = (Label) dayGrid.getWidget(i, j);
				if (l == null) {
					l = new Label();
					dayGrid.setWidget(i, j, l);
				} else {
					removeAllStyles(l);
				}
				if (k > dayNumber) {
					dayGrid.remove(l);
				} else {
					l.setText("" + k);
				}
				if (model.getSelectionCardinality() == SelectionCardinality.NONE) {
					l.addStyleName(theme.getDisabledGridCellStyle()
							.getClassName());
				} else {
					l.addStyleName(theme.getCalendarGridCellStyle()
							.getClassName());
				}
				if (controller.isDisplayableMin(month, year, k) == false
						|| controller.isDisplayableMax(month, year, k) == false) {
					l.addStyleName(theme.getForbiddenStyle().getClassName());
				}
				l.setTitle(ToolTipProvider.getToolTipForDate(year, month, k));
				k++;
			}
		}
	}

	/**
	 * Fills days of the next month in the grid
	 * 
	 * @param g
	 * @param year
	 * @param month
	 */

	public void fillNextMonthDays(Grid g, int year, int month) {
		int ll = getLastFilledLine(g);
		if (ll == -1) {
			return;
		}
		int nextDay = 1;
		for (int i = 0; i < g.getColumnCount(); i++) {
			if (g.getWidget(ll, i) != null) {
				continue;
			}
			Label l = new Label("" + nextDay);
			l.setTitle(ToolTipProvider.getToolTipForDate(year, month + 1,
					nextDay));
			if (model.getSelectionCardinality() == SelectionCardinality.NONE) {
				l.addStyleName(theme.getDisabledGridCellStyle().getClassName());
			} else {
				l.addStyleName(theme.getPreviousAndNextDayStyle()
						.getClassName());
			}
			g.setWidget(ll, i, l);
			nextDay++;
		}
	}

	/* Events Management Methods (Calendar Events) */

	/**
	 * Applies styles about current day and event styles
	 * 
	 * @param month
	 * @param year
	 */

	public void applyStyles(int month, int year) {
		// Apply a style on the current day
		if (month == model.getCurrentMonth() && year == model.getCurrentYear()) {
			applyStyleOnCurrentDay();
		}
		CalendarEventManager manager = model.getManager();
		// Apply a style on special and busy days (according to user events)
		for (Event e : manager.getEvents()) {
			Period p = manager.getEventPeriodDuringMonth(e, month, year);
			applyEventOnPeriod(e, p);
		}
		// Apply a style on selected dates (previously)
		applyStylesOnSelectedDates();
	}

	/**
	 * Apply a style on the current day label in the grid
	 */

	private void applyStyleOnCurrentDay() {
		int currentYear = model.getCurrentYear();
		int currentMonth = model.getCurrentMonth();
		int displayedYear = model.getDisplayedYear();
		int displayedMonth = model.getDisplayedMonth();
		int currentDay = model.getCurrentDay();

		if (displayedMonth != currentMonth || displayedYear != currentYear) {
			return;
		}
		if (getMonthDayLabel(currentDay) != null) {
			getMonthDayLabel(currentDay).addStyleName(
					theme.getCurrentDayStyle().getClassName());
		}
	}

	/**
	 * Applies an event on a period
	 * 
	 * @param e
	 * @param p
	 */

	private void applyEventOnPeriod(Event e, Period p) {
		if (p == null) {
			return;
		}
		if (p.getBeginDate().getYear() + 1900 != model.getDisplayedYear()
				&& e.getFrequency() == EventFrequency.NONE) {
			return;
		}
		int beginDate = p.getBeginDate().getDate();
		int endDate = p.getEndDate().getDate();
		if (beginDate <= endDate) {
			for (int m = beginDate; m <= endDate; m++) {
				Label l = getMonthDayLabel(m);
				if (l == null) {
					continue;
				}
				applyEventOnDate(l, e);
			}
		}
	}

	/**
	 * Applies an event on a date
	 * 
	 * @param l
	 * @param e
	 */

	private void applyEventOnDate(Label l, Event e) {
		if (l == null) {
			return;
		}
		CssStyle css = mapper.getStyle(e.getEventType());
		if (css == null) {
			return;
		}
		l.setStyleName(css.getClassName());
		l.setTitle(l.getTitle() + " - "
				+ ToolTipProvider.getShortToolTipForEvent(e));
	}

	/**
	 * Applies a style on previously selected dates
	 */

	private void applyStylesOnSelectedDates() {
		if (model.getSelectionCardinality() == SelectionCardinality.NONE) {
			return;
		}
		if (model.getSelectionCardinality() == SelectionCardinality.SINGLE) {
			Date d = model.getSelectedDate();
			if (d == null) {
				return;
			}
			int date = d.getDate();
			int month = d.getMonth();
			int year = d.getYear() + 1900;
			if (model.getDisplayedMonth() == month
					&& model.getDisplayedYear() == year
					&& isSelectable(date) == true) {
				getMonthDayLabel(date).addStyleName(
						theme.getSelectedGridCellStyle().getClassName());
			}
			return;
		}

		/* Otherwise */
		Date begin = model.peekDisplayedDate();
		begin.setDate(1);
		Date end = model.peekDisplayedDate();
		end.setDate(CalendarUtils.getDaysNumber(begin.getYear() + 1900, begin
				.getMonth()));

		List<Date> dates = model.getSelectedDates(begin, end);
		for (Date d : dates) {
			Label l = getMonthDayLabel(d.getDate());
			if (isSelectable(l) == false) {
				continue;
			}
			l.addStyleName(theme.getSelectedGridCellStyle().getClassName());
		}
	}

	private void removeAllStyles(Label l) {
		String[] styles = l.getStyleName().split(" ");
		for (int i = 0; i < styles.length; i++) {
			l.removeStyleName(styles[i]);
		}
	}

	/* Grid Methods */

	/**
	 * Returns the label thats represents a day <i>day</i> in the grid
	 * 
	 * @param day
	 * @return
	 */

	private Label getMonthDayLabel(int day) {
		for (int i = 1; i < dayGrid.getRowCount(); i++) {
			for (int j = 0; j < dayGrid.getColumnCount(); j++) {
				Label l = (Label) dayGrid.getWidget(i, j);
				if (l == null) {
					continue;
				}
				if (l.getText().equals("") == true) {
					continue;
				}
				int d = Integer.parseInt(l.getText());
				if (i == 1 && d > 7) {
					continue;
				}
				if (l.getStyleName().contains(
						theme.getPreviousAndNextDayStyle().getClassName()) == true) {
					continue;
				}
				if (i > 3 && d < 7) {
					continue;
				}
				if (d == day) {
					return l;
				}
			}
		}
		return null;
	}

	/**
	 * Returns the last non-null line in the grid
	 * 
	 * @param g
	 *            The grid to explore
	 * @return the last filled line number
	 */

	private int getLastFilledLine(Grid g) {
		int i = 0;
		for (; i < g.getRowCount(); i++) {
			if (g.getWidget(i, 0) == null) {
				return i - 1;
			}
		}
		return i - 1;
	}

	/**
	 * Returns the row number of a day in the grid
	 * 
	 * @param day
	 * @return
	 */

	private int getRowForDay(int day) {
		for (int i = 1; i < dayGrid.getRowCount(); i++) {
			for (int j = 0; j < dayGrid.getColumnCount(); j++) {
				Label l = (Label) dayGrid.getWidget(i, j);
				if (l == null) {
					continue;
				}
				int n = Integer.parseInt(l.getText());
				if (n == day) {
					return i;
				}
			}
		}
		return -1;
	}

	/**
	 * Returns the row number of the last <i>day</i> in the grid
	 * 
	 * @param day
	 * @return
	 */

	private int getRowForLastDay(int day) {
		int r = -1;
		for (int i = 1; i < dayGrid.getRowCount(); i++) {
			for (int j = 0; j < dayGrid.getColumnCount(); j++) {
				Label l = (Label) dayGrid.getWidget(i, j);
				if (l == null) {
					continue;
				}
				int n = Integer.parseInt(l.getText());
				if (n == day) {
					r = i;
				}
			}
		}
		return r;
	}

	/**
	 * Returns a column number of a day in the grid
	 * 
	 * @param day
	 * @return
	 */

	private int getColumnForDay(int day) {
		for (int i = 1; i < dayGrid.getRowCount(); i++) {
			for (int j = 0; j < dayGrid.getColumnCount(); j++) {
				Label l = (Label) dayGrid.getWidget(i, j);
				if (l == null) {
					continue;
				}
				if (Integer.parseInt(l.getText()) == day) {
					return j;
				}
			}
		}
		return -1;
	}

	/**
	 * Returns a column number of the last <i>day</i> in the grid
	 * 
	 * @param day
	 * @return
	 */

	private int getColumnForLastDay(int day) {
		int r = -1;
		for (int i = 1; i < dayGrid.getRowCount(); i++) {
			for (int j = 0; j < dayGrid.getColumnCount(); j++) {
				Label l = (Label) dayGrid.getWidget(i, j);
				if (l == null) {
					continue;
				}
				if (Integer.parseInt(l.getText()) == day) {
					r = j;
				}
			}
		}
		return r;
	}

	/* Selection Methods */

	/**
	 * Clears the selection in the grid (remove selection styles on all labels)
	 */

	private void clearSelection() {
		if (model.getSelectedDates().isEmpty() == true) {
			return;
		}
		unfocusWidget(getMonthDayLabel(model.getCursorPosition()));
		if (isControlKeyDown == false) {
			Iterator<Date> it = model.getSelectedDates().iterator();
			for (; it.hasNext();) {
				Date d = it.next();
				unselectCell(0, getColumnForDay(d.getDate()));
				Label l = getMonthDayLabel(d.getDate());
				if (l == null) {
					continue;
				}
				l.removeStyleName(theme.getSelectedGridCellStyle()
						.getClassName());
				it.remove();
			}
		}
	}

	/**
	 * Selects a cell
	 * 
	 * @param i
	 * @param j
	 */

	private void selectCell(int i, int j) {
		if (i >= dayGrid.getRowCount() || j >= dayGrid.getCellCount(j)) {
			return;
		}
		Label w = (Label) dayGrid.getWidget(i, j);
		if (w == null) {
			return;
		}
		if (i == 0) {
			w.addStyleName(theme.getSelectedGridCellStyle().getClassName());
			return;
		}
		int n = Integer.parseInt(w.getText());
		/* If the cell is selectable */
		if (isSelectable(w) == true) {
			manageEventCellStylesForSelection(w);
			if (model.getSelectionCardinality() != SelectionCardinality.MULTIPLE) {
				return;
			}
			Date d = model.peekDisplayedDate();
			d.setDate(n);
			model.addSelectedDate(d);
			controller.launchSelectionChangedEvent();
			controller.setSelection(n);
		} else {
			Date d = model.peekDisplayedDate();
			d.setDate(n);
			controller.fireInvalidDateEvent(d);
		}
	}

	/**
	 * Manages styles for grid cell with events
	 * 
	 * @param l
	 */

	private void manageEventCellStylesForSelection(Label l) {
		CssStyle[] styles = theme.getEventStyles();
		for (int i = 0; i < styles.length; i++) {
			CssStyle style = styles[i];
			if (l.getStyleName().contains(style.getClassName()) == true) {
				l.removeStyleName(style.getClassName());
				l.addStyleName(theme.getSelectedGridCellStyle().getClassName());
				l.addStyleName(style.getClassName());
			} else {
				l.addStyleName(theme.getSelectedGridCellStyle().getClassName());
			}
		}
	}

	/**
	 * Deselects a cell
	 * 
	 * @param i
	 * @param j
	 */

	private void unselectCell(int i, int j) {
		if (i >= dayGrid.getRowCount() || j >= dayGrid.getCellCount(j)) {
			return;
		}
		Label l = (Label) dayGrid.getWidget(i, j);
		if (l == null) {
			return;
		}
		l.removeStyleName(theme.getSelectedGridCellStyle().getClassName());
		if (i != 0) {
			int n = Integer.parseInt(l.getText());
			Date d = model.peekDisplayedDate();
			d.setDate(n);
			controller.removeDeselectedDate(d);
			controller.launchSelectionChangedEvent();
		}
	}

	/**
	 * Returns true if the cell is selected, false otherwise
	 * 
	 * @param i
	 * @param j
	 * @return
	 */

	private boolean isSelectedCell(int i, int j) {
		Widget w = dayGrid.getWidget(i, j);
		if (w == null) {
			return false;
		}
		return w.getStyleName().contains(
				theme.getSelectedGridCellStyle().getClassName());
	}

	/**
	 * Selects a label
	 * 
	 * @param l
	 */

	private void focusWidget(Widget w) {
		if (w != null) {
			Label l = (Label) w;
			if (isNavigable(l) == true) {
				w.addStyleName(theme.getCursorGridCellStyle().getClassName());
			}
		}
	}

	/**
	 * Deselects a label
	 * 
	 * @param l
	 */

	private void unfocusWidget(Widget l) {
		if (l != null) {
			l.removeStyleName(theme.getCursorGridCellStyle().getClassName());
		}
	}

	/**
	 * Selects a day range (date)
	 * 
	 * @param begin
	 * @param end
	 */

	private void selectRange(int begin, int end) {
		/* If the selection is by left to right */
		if (begin < end) {
			int row = getRowForLastDay(begin);
			int col = getColumnForLastDay(begin);
			for (int i = begin; i <= end - 1; i++) {
				if (isSelectedCell(row, col) == true) {
					unselectCell(row, col);
				} else {
					selectCell(row, col);
				}
				if (col >= dayGrid.getColumnCount() - 1) {
					row++;
					col = 0;
				} else {
					col++;
				}
			}
		}
		/* If the selection is by right to left */
		else if (begin > end) {
			int row = getRowForDay(end + 1);
			int col = getColumnForDay(end + 1);
			for (int i = end + 1; i <= begin; i++) {
				if (isSelectedCell(row, col) == true) {
					unselectCell(row, col);
				} else {
					selectCell(row, col);
				}
				if (col >= dayGrid.getColumnCount() - 1) {
					row++;
					col = 0;
				} else {
					col++;
				}
			}
		} else {
			return;
		}
	}

	private void selectRange(int beginRow, int beginCol, int endRow, int endCol) {
		if (beginRow < endRow) {
			int row = beginRow;
			int col = beginCol;
			while (row != endRow || col != endCol) {
				Label l = (Label) dayGrid.getWidget(row, col);
				if (l == null) {
					continue;
				}
				if (isSelectedCell(row, col) == true) {
					unselectCell(row, col);
				} else {
					selectCell(row, col);
				}
				if (col >= dayGrid.getColumnCount() - 1) {
					row++;
					col = 0;
				} else {
					col++;
				}
			}
		} else if (beginRow > endRow) {
			int row = endRow;
			int col = endCol;
			while (row != beginRow || col != beginCol) {
				Label l = (Label) dayGrid.getWidget(row, col);
				if (l == null) {
					continue;
				}
				if (isSelectedCell(row, col) == true) {
					unselectCell(row, col);
				} else {
					selectCell(row, col);
				}
				if (col >= dayGrid.getColumnCount() - 1) {
					row++;
					col = 0;
				} else {
					col++;
				}
			}
		} else if (beginRow == endRow) {
			if (beginCol > endCol) {
				int row = endRow;
				int col = endCol;
				while (row != beginRow || col != beginCol) {
					Label l = (Label) dayGrid.getWidget(row, col);
					if (l == null) {
						continue;
					}
					if (isSelectedCell(row, col) == true) {
						unselectCell(row, col);
					} else {
						selectCell(row, col);
					}
					if (col >= dayGrid.getColumnCount() - 1) {
						row++;
						col = 0;
					} else {
						col++;
					}
				}
			} else if (beginCol < endCol) {
				int row = beginRow;
				int col = beginCol;
				while (row != endRow || col != endCol) {
					Label l = (Label) dayGrid.getWidget(row, col);
					if (l == null) {
						continue;
					}
					if (isSelectedCell(row, col) == true) {
						unselectCell(row, col);
					} else {
						selectCell(row, col);
					}
					if (col >= dayGrid.getColumnCount() - 1) {
						row++;
						col = 0;
					} else {
						col++;
					}
				}
			}
		}
	}

	/**
	 * Selects columns in a range (days)
	 * 
	 * @param previousSelectedColumn
	 * @param cell
	 */

	private void selectColumnRange(int begin, int end) {
		if (begin < end) {
			for (int i = begin; i <= end; i++) {
				for (int j = 0; j < dayGrid.getRowCount(); j++) {
					selectCell(j, i);
				}
			}
		} else if (begin > end) {
			for (int i = end; i <= begin; i++) {
				for (int j = 0; j < dayGrid.getRowCount(); j++) {
					selectCell(j, i);
				}
			}
		} else {
			return;
		}
	}

	/**
	 * Selects rows in a range (week)
	 * 
	 * @param previousSelectedColumn
	 * @param cell
	 */

	private void selectRowRange(int begin, int end) {
		if (begin < end) {
			for (int i = begin; i <= end; i++) {
				for (int j = 0; j < dayGrid.getColumnCount(); j++) {
					selectCell(i, j);
				}
			}
		} else if (begin > end) {
			for (int i = end; i <= begin; i++) {
				for (int j = 0; j < dayGrid.getColumnCount(); j++) {
					selectCell(i, j);
				}
			}
		} else {
			return;
		}
	}

	/**
	 * Returns the current selection
	 * 
	 * @return
	 */

	public Widget getCurrentSelection() {

		SelectionType context = model.getSelectionType();
		if (context == SelectionType.DATE) {
			return getMonthDayLabel(model.getCursorPosition());
		} else if (context == SelectionType.DAYS) {
			for (int i = 0; i < dayGrid.getCellCount(0); i++) {
				if (isSelectedCell(0, i) == true) {
					return dayGrid.getWidget(0, i);
				}
			}
		}
		return null;
	}

	/**
	 * Returns the next navigable position from <i>pos</i>
	 * 
	 * @param pos
	 * @return
	 */

	private int getNextNavigablePosition(int pos) {
		int i = pos + 1;
		if (pos == 1) {
			i = pos;
		}
		int dn = CalendarUtils.getDaysNumber(model.peekDisplayedDate());
		for (; i < dn; i++) {
			if (isNavigable(i) == true) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Returns the previous navigable position from <i>pos</i>
	 * 
	 * @param pos
	 * @return
	 */

	private int getPreviousNavigablePosition(int pos) {
		int pnp = -1;
		for (int i = 0; i < pos; i++) {
			if (isNavigable(i) == true) {
				pnp = i;
			}
		}
		return pnp;
	}

	/**
	 * Returns the first navigable position
	 * 
	 * @return
	 */

	private int getFirstNavigablePosition() {
		return getNextNavigablePosition(1);
	}

	/**
	 * Returns the last navigable position
	 * 
	 * @return
	 */

	private int getLastNavigablePosition() {
		return getPreviousNavigablePosition(CalendarUtils.getDaysNumber(model
				.getDisplayedYear(), model.getDisplayedMonth()) + 1);
	}

	/**
	 * Returns the first navigable position (for a month) in the grid
	 * 
	 * @param column
	 * @return
	 */

	private int getFirstNavigablePositionforDays(int column) {
		for (int i = 1; i < dayGrid.getRowCount(); i++) {
			Label l = (Label) dayGrid.getWidget(i, column);
			if (isNavigable(l) == true) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Returns the last navigable position (for a month) in the grid
	 * 
	 * @param column
	 * @return
	 */

	private int getLastNavigablePositionforDays(int column) {
		for (int i = getLastFilledLine(dayGrid); i > 0; i--) {
			Label l = (Label) dayGrid.getWidget(i, column);
			if (isNavigable(l) == true) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * Returns true if a day in the grid is selectable
	 * 
	 * @param day
	 * @return
	 */

	private boolean isSelectable(int i) {
		Label l = getMonthDayLabel(i);
		return isSelectable(l);
	}

	/**
	 * Returns true if a label in the grid is selectable
	 * 
	 * @param day
	 * @return
	 */

	private boolean isSelectable(Widget w) {
		Label l = (Label) w;
		if (l == null) {
			return false;
		}
		// PREVIOUS & NEXT DAYS = NON - SELECTABLE
		if (l.getStyleName().contains(
				theme.getPreviousAndNextDayStyle().getClassName()) == true) {
			return false;
		}
		// FORBIDDEN = NON - SELECTABLE
		if (l.getStyleName().contains(theme.getForbiddenStyle().getClassName()) == true) {
			return false;
		}
		return mapper.isSelectable(w.getStyleName());
	}

	/**
	 * Returns true if a day in the grid is navigable
	 * 
	 * @param day
	 * @return
	 */

	private boolean isNavigable(int day) {
		Label l = getMonthDayLabel(day);
		return isNavigable(l);
	}

	/**
	 * Returns true if a label is navigable
	 * 
	 * @param l
	 * @return
	 */

	private boolean isNavigable(Label l) {
		if (l == null) {
			return false;
		}
		// PREVIOUS & NEXT DAYS = NON-NAVIGABLE
		if (l.getStyleName().contains(
				theme.getPreviousAndNextDayStyle().getClassName()) == true) {
			return false;
		}
		// FORBIDDEN = NON - NAVIGABLE
		if (l.getStyleName().contains(theme.getForbiddenStyle().getClassName()) == true) {
			return false;
		}
		return mapper.isNavigable(l.getStyleName());
	}

	/**
	 * Manages the selection into a context
	 */

	private void nextContextElement() {

		SelectionType type = model.getSelectionType();
		/*
		 * If the selection context is on DATE cursor : the date changes
		 * according to the dates navigable capability
		 */
		switch (type) {
		case DATE:
			unfocusWidget(getMonthDayLabel(model.getCursorPosition()));
			if (model.getCursorPosition() < CalendarUtils.getDaysNumber(model
					.getDisplayedYear(), model.getDisplayedMonth())) {
				Date nextDate = model.peekDisplayedDate();
				if (isNavigable(model.getCursorPosition() + 1) == true) {
					nextDate.setDate(model.getCursorPosition() + 1);
					focusWidget(getMonthDayLabel(model.getCursorPosition() + 1));
					controller.increaseSelection();
				} else {
					int n = getNextNavigablePosition(model.getCursorPosition() + 1);
					if (n != -1) {
						nextDate.setDate(n);
						focusWidget(getMonthDayLabel(n));
						controller.setSelection(n);
					} else {
						controller.handle(CalendarAction.NEXT_MONTH);
						nextDate = model.peekDisplayedDate();
						if (isNavigable(1) == true) {
							nextDate.setDate(1);
							focusWidget(getMonthDayLabel(1));
							controller.setSelection(1);
						} else {
							int nn = getNextNavigablePosition(1);
							nextDate.setDate(nn);
							focusWidget(getMonthDayLabel(nn));
							controller.setSelection(nn);
						}
					}
				}
			} else {
				controller.handle(CalendarAction.NEXT_MONTH);
				Date nextDate = new Date();
				nextDate.setYear(model.getDisplayedYear());
				nextDate.setMonth(model.getDisplayedMonth());
				nextDate.setDate(1);
				focusWidget(getMonthDayLabel(1));
				controller.setSelection(1);
			}
			break;
		case DAYS:
			if (isControlKeyDown == false) {
				for (int i = 0; i < dayGrid.getRowCount(); i++) {
					unselectCell(i, model.getSelectedColumn());
				}
			}
			controller.setSelectedColumn(model.getSelectedColumn() + 1);
			if (model.getSelectedColumn() < CalendarModel.getDaysInWeek()) {
				for (int i = 0; i < dayGrid.getRowCount(); i++) {
					selectCell(i, model.getSelectedColumn());
				}
			} else {
				controller.handle(CalendarAction.NEXT_MONTH);
				controller.setSelectedColumn(0);
				for (int i = 0; i < dayGrid.getRowCount(); i++) {
					selectCell(i, model.getSelectedColumn());
				}
			}
			break;
		}
	}

	/**
	 * Manages the selection context (for previous context action)
	 **/

	private void previousContextElement() {
		SelectionType type = model.getSelectionType();
		/*
		 * If the selection context is on DATE cursor : the date changes
		 * according to the dates navigable capability
		 */
		switch (type) {
		case DATE:
			unfocusWidget(getMonthDayLabel(model.getCursorPosition()));
			if (model.getCursorPosition() <= CalendarUtils.getDaysNumber(model
					.getDisplayedYear(), model.getDisplayedMonth())) {
				Date nextDate = model.peekDisplayedDate();
				if (isNavigable(model.getCursorPosition() - 1) == true) {
					nextDate.setDate(model.getCursorPosition() - 1);
					focusWidget(getMonthDayLabel(model.getCursorPosition() - 1));
					controller.decreaseSelection();
				} else {
					int n = getPreviousNavigablePosition(model
							.getCursorPosition() - 1);
					if (n != -1) {
						nextDate.setDate(n);
						focusWidget(getMonthDayLabel(n));
						controller.setSelection(n);
					} else {
						controller.handle(CalendarAction.PREVIOUS_MONTH);
						nextDate = model.peekDisplayedDate();
						int dn = CalendarUtils.getDaysNumber(model
								.getDisplayedYear(), model.getDisplayedMonth());
						if (isNavigable(dn) == true) {
							nextDate.setDate(dn);
							focusWidget(getMonthDayLabel(dn));
							controller.setSelection(dn);
						} else {
							int nn = getPreviousNavigablePosition(dn);
							nextDate.setDate(nn);
							focusWidget(getMonthDayLabel(nn));
							controller.setSelection(nn);
						}
					}
				}
			} else {
				controller.handle(CalendarAction.PREVIOUS_MONTH);
				int dayNumber = CalendarUtils.getDaysNumber(model
						.getDisplayedYear(), model.getDisplayedMonth());
				Date previousDate = new Date();
				previousDate.setYear(model.getDisplayedYear());
				previousDate.setMonth(model.getDisplayedMonth());
				previousDate.setDate(dayNumber);
				focusWidget(getMonthDayLabel(dayNumber));
				controller.setSelection(dayNumber);
			}
			break;
		case DAYS:
			if (isControlKeyDown == false) {
				for (int i = 0; i < dayGrid.getRowCount(); i++) {
					unselectCell(i, model.getSelectedColumn());
				}
			}
			controller.setSelectedColumn(model.getSelectedColumn() - 1);
			if (model.getSelectedColumn() >= 0) {
				for (int i = 0; i < dayGrid.getRowCount(); i++) {
					selectCell(i, model.getSelectedColumn());
				}
			} else {
				controller.handle(CalendarAction.PREVIOUS_MONTH);
				controller.setSelectedColumn(CalendarModel.getDaysInWeek() - 1);
				for (int i = 0; i < dayGrid.getRowCount(); i++) {
					selectCell(i, model.getSelectedColumn());
				}
			}
			break;
		}
	}

	@Override
	public void onKeyDown(KeyDownEvent event) {
		isControlKeyDown = event.isControlKeyDown();
		isShiftKeyDown = event.isShiftKeyDown();

		if (model.getSelectionType() != SelectionType.DATE
				&& model.getSelectionType() != SelectionType.DAYS) {
			return;
		}
		int keyCode = event.getNativeKeyCode();
		if (keyCode != KeyCodes.KEY_TAB) {
			event.preventDefault();
		}

		if (model.getSelectionType() == SelectionType.DAYS
				&& (keyCode == KeyCodes.KEY_UP || keyCode == KeyCodes.KEY_DOWN)) {
			return;
		}

		int selection = model.getCursorPosition();

		int selectionRow = getRowForDay(selection);
		int selectionColumn = getColumnForDay(selection);

		int lastSelectionRow = 0;
		int lastSelectionColumn = 0;

		if (isControlKeyDown == false && isShiftKeyDown == false) {
			lastSelectionRow = getRowForLastDay(selection);
			lastSelectionColumn = getColumnForLastDay(selection);
		}

		switch (keyCode) {
		case KeyCodes.KEY_HOME:
			/* When the HOME key is pressed */

			unfocusWidget(getMonthDayLabel(selection));
			if (selection == getFirstNavigablePosition()) {
				controller.handle(CalendarAction.PREVIOUS_MONTH);
			}
			int fnp = getFirstNavigablePosition();
			focusWidget(getMonthDayLabel(fnp));
			controller.setSelection(fnp);
			break;
		case KeyCodes.KEY_END:
			/* When the END key is pressed */
			unfocusWidget(getMonthDayLabel(selection));
			if (selection == getLastNavigablePosition()) {
				controller.handle(CalendarAction.NEXT_MONTH);
			}
			int lnp = getLastNavigablePosition();
			focusWidget(getMonthDayLabel(lnp));
			controller.setSelection(lnp);
			break;
		case KeyCodes.KEY_PAGEUP:
			/* When the PAGE_UP key is pressed */
			int column = getColumnForDay(selection);
			int row = getFirstNavigablePositionforDays(column);
			Label l = (Label) getMonthDayLabel(selection);
			unfocusWidget(l);
			l = (Label) dayGrid.getWidget(row, column);
			if (l == null) {
				return;
			}
			focusWidget(l);
			controller.setSelection(Integer.parseInt(l.getText()));
			break;
		case KeyCodes.KEY_PAGEDOWN:
			/* When the PAGE_DOWN key is pressed */
			int lnp_row = getLastNavigablePositionforDays(selectionColumn);
			Label sel_lab = getMonthDayLabel(selection);
			unfocusWidget(sel_lab);
			sel_lab = (Label) dayGrid.getWidget(lnp_row, selectionColumn);
			if (sel_lab == null) {
				return;
			}
			focusWidget(sel_lab);
			controller.setSelection(Integer.parseInt(sel_lab.getText()));
			break;
		case KeyCodes.KEY_UP:
			/* When the UP key is pressed */
			Label lab = (Label) dayGrid
					.getWidget(selectionRow, selectionColumn);
			if (selectionRow > 1 && selectionRow <= getLastFilledLine(dayGrid)) {
				unfocusWidget(lab);
				Label w = (Label) dayGrid.getWidget(selectionRow - 1,
						selectionColumn);
				if (selectionRow >= 2 && isNavigable(w) == false) {
					unselectCell(selectionRow, selectionColumn);
					controller.handle(CalendarAction.PREVIOUS_MONTH);
					int r = getLastFilledLine(dayGrid);
					Label lab2 = (Label) dayGrid.getWidget(r, selectionColumn);
					while (isNavigable(lab2) == false) {
						lab2 = (Label) dayGrid.getWidget(r--, selectionColumn);
					}
					if (isNavigable(lab2) == true) {
						focusWidget(lab2);
						controller.setSelection(Integer
								.parseInt(lab2.getText()));
					}
					return;
				}
				if (w == null) {
					return;
				}
				focusWidget(w);
				controller.setSelection(Integer.parseInt(w.getText()));
			} else if (selectionRow == 1) {
				unselectCell(selectionRow, selectionColumn);
				controller.handle(CalendarAction.PREVIOUS_MONTH);
				int r = getLastFilledLine(dayGrid);
				Label lab2 = (Label) dayGrid.getWidget(r, selectionColumn);
				while (isNavigable(lab2) == false) {
					lab2 = (Label) dayGrid.getWidget(r--, selectionColumn);
				}
				if (isNavigable(lab2) == true) {
					focusWidget(lab2);
					controller.setSelection(Integer.parseInt(lab2.getText()));
				}
			}
			break;
		case KeyCodes.KEY_DOWN:
			/* When the DOWN key is pressed */
			Label lab3 = (Label) dayGrid.getWidget(selectionRow,
					selectionColumn);
			if (selectionRow > 0 && selectionRow < getLastFilledLine(dayGrid)) {
				unfocusWidget(lab3);
				Label w = (Label) dayGrid.getWidget(selectionRow + 1,
						selectionColumn);
				if (w == null) {
					return;
				}
				if (selectionRow == getLastFilledLine(dayGrid) - 1
						&& isNavigable(w) == false) {
					unselectCell(lastSelectionRow, lastSelectionColumn);
					controller.handle(CalendarAction.NEXT_MONTH);
					int r = 1;
					Label lab4 = (Label) dayGrid.getWidget(r,
							lastSelectionColumn);
					while (isNavigable(lab4) == false) {
						lab4 = (Label) dayGrid.getWidget(r++,
								lastSelectionColumn);
					}
					if (isNavigable(lab4) == true) {
						focusWidget(lab4);
						controller.setSelection(Integer
								.parseInt(lab4.getText()));
					}
					return;
				}
				while (isNavigable(w) == false
						&& selectionRow < getLastFilledLine(dayGrid)) {
					w = (Label) dayGrid.getWidget(selectionRow++,
							selectionColumn);
				}
				if (isNavigable(w) == true) {
					focusWidget(w);
					controller.setSelection(Integer.parseInt(w.getText()));
				} else {
					controller.handle(CalendarAction.NEXT_MONTH);
				}
			} else if (lastSelectionRow == getLastFilledLine(dayGrid)) {
				unselectCell(lastSelectionRow, lastSelectionColumn);
				controller.handle(CalendarAction.NEXT_MONTH);
				int r = 1;
				Label lab4 = (Label) dayGrid.getWidget(r, lastSelectionColumn);
				while (isNavigable(lab4) == false) {
					lab4 = (Label) dayGrid.getWidget(r++, lastSelectionColumn);
				}
				if (isNavigable(lab4) == true) {
					focusWidget(lab4);
					controller.setSelection(Integer.parseInt(lab4.getText()));
				}
			}
			break;
		case KeyCodes.KEY_LEFT:
			/* When the LEFT key is pressed */
			previousContextElement();
			break;
		case KeyCodes.KEY_RIGHT:
			/* When the RIGHT key is pressed */
			nextContextElement();
			break;
		case KeyCodes.KEY_ENTER:
			/* When an ENTER key is pressed (Selected item validation) */
			Date d = model.peekDisplayedDate();
			d.setDate(model.getCursorPosition());
			if (d == null || isSelectable(d.getDate()) == false) {
				return;
			}
			/* SINGLE Selection Type */
			if (model.getSelectionCardinality() == SelectionCardinality.SINGLE) {
				int prev = model.getSelectedDate().getDate();
				int n = d.getDate();
				unselectCell(getRowForDay(prev), getColumnForDay(prev));
				controller.setSelectedDate(d);
				controller.setSelection(n);
				selectCell(getRowForDay(n), getColumnForDay(n));
				return;
			}
			/* If the CONTROL KEY is not pressed : clear the selection */
			if (isControlKeyDown == false) {
				clearSelection();
			}
			/* If the selection is on days */
			if (model.getSelectionType() == SelectionType.DAYS) {
				if (isShiftKeyDown == true) {
					int begin = Integer.parseInt(((Label) dayGrid.getWidget(1,
							model.getSelectedColumn())).getText());
					int end = d.getDate();
					selectRange(begin, end);
				}
				int Datecolumn = getColumnForDay(d.getDate());
				for (int i = 1; i < dayGrid.getRowCount(); i++) {
					selectCell(i, Datecolumn);
				}
				return;
			}
			/* If the SHIFT KEY is pressed : selects range */
			if (isShiftKeyDown == true) {
				int previous = model.getSelectedDate().getDate();
				int sel = d.getDate();
				selectRange(previous, sel);
				controller.setSelectedDate(d);
				controller.setSelection(sel);
			}
			/* DATE CASE */
			int dateRow = getRowForDay(d.getDate());
			int dateCell = getColumnForDay(d.getDate());
			controller.setSelectedDate(d);
			if (isSelectedCell(dateRow, dateCell) == true) {
				unselectCell(dateRow, dateCell);
			} else {
				selectCell(dateRow, dateCell);
			}
			break;
		}
	}

	@Override
	public void onKeyUp(KeyUpEvent event) {
		int keyCode = event.getNativeKeyCode();
		if (keyCode == KeyCodes.KEY_CTRL) {
			isControlKeyDown = false;
		} else if (keyCode == KeyCodes.KEY_SHIFT) {
			isShiftKeyDown = false;
		}
	}

	@Override
	public void onMouseWheel(MouseWheelEvent event) {
		SelectionType type = model.getSelectionType();
		if (type != SelectionType.DATE && type != SelectionType.DAYS) {
			return;
		}
		event.preventDefault();
		if (type == SelectionType.DATE) {
			int selection = model.getSelectedDate().getDate();
			if (event.isNorth() == true) {
				controller.handle(CalendarAction.PREVIOUS_MONTH);
				selectCell(getRowForDay(selection), getColumnForDay(selection));
			} else if (event.isSouth() == true) {
				controller.handle(CalendarAction.NEXT_MONTH);
				selectCell(getRowForDay(selection), getColumnForDay(selection));
			}
		}
		if (type == SelectionType.DAYS) {
			if (event.isNorth() == true) {
				previousContextElement();
			} else if (event.isSouth() == true) {
				nextContextElement();
			}
		}
	}
}